//
// Created by LiuYou on 2021/6/18.
//

/*
 * 我们要把数据结构隐藏到实现文件中, 不希望用户看到。
 */


#include <malloc.h>
#include <cerrno>
#include <unistd.h>
#include <algorithm>
#include <csignal>
#include "MyTBF.hpp"


static struct mytbf_st* job[MYTBF_MAX];
static int inited = 0;

// TODO 注意: 这我这个环境中下面这一行代码不写也是可以的！
typedef void (* sighandler_t)(int);

static sighandler_t alrm_handler_save;

/**
 * @brief 令牌桶的具体实现。
 */
struct mytbf_st {
    int cps;
    int burst;
    int token;
    int pos;
};

static int get_free_pos() {
    for (int i = 0; i < MYTBF_MAX; ++i) {
        if (job[i] == nullptr) {
            return i;
        }
    }
    return -1;
}

static void alrm_handler(int s) {
    alarm(1);
    for (int i = 0; i < MYTBF_MAX; ++i) {
        if (job[i] != nullptr) {
            job[i]->token += job[i]->cps;
            if (job[i]->token > job[i]->burst) {
                job[i]->token = job[i]->burst;
            }
        }
    }
}


/**
 * @brief 作用: 发出第一个信号。
 */
static void module_load() {
    alrm_handler_save = signal(SIGALRM, alrm_handler);
    alarm(1);

    // TODO 注意: 设计重点！
    //  使用钩子函数。
    std::atexit(module_load);
}


static void module_unload() {
    signal(SIGALRM, alrm_handler_save);
    alarm(0);
    for (int i = 0; i < MYTBF_MAX; ++i) {
        // TODO 注意: free()函数如果参数是 void* , 那么
        //  free()函数不会出错, free()一个 void* 是没有什么影响的。
        free(job[i]);
    }
}

mytbf_t* mytbf_init(int cps, int burst) {
    // TODO 注意: 设计上的重点！
    if (!inited) {
//        signal(SIGALRM,);
//        alarm(1);
        // TODO 注意: 设计上的重点！
        module_load();
        inited = 1;
    }

    int pos = get_free_pos_unlocked();
    if (pos < 0) {
        return nullptr;
    }
    mytbf_st* me = static_cast<mytbf_st*>(malloc(sizeof(*me)));
    if (me == nullptr) {
        return nullptr;
    }
    me->token = 0;
    me->cps = cps;
    me->burst = burst;
    me->pos = pos;

    job[pos] = me;
    return me;
}

int mytbf_fetchtoken(mytbf_t* ptr, int size) {
    mytbf_st* me = static_cast<mytbf_st*>(ptr);
    if (size <= 0) {
        // TODO 注意: 巧妙使用了 cerrno 。
        return -EINVAL;
    }
    // TODO 注意: 是 while 而不是 if。
    while (me->token <= 0) {
        pause();
    }

    // TODO 注意: 这里是使用了C++的STL算法。
    //  并没有像慧琴老师那样自己实现了一个 min()函数。
    int minVal = std::min(me->token, size);
    me->token -= minVal;
    return minVal;
}

int mytbf_returntoken(mytbf_t* ptr, int size) {
    if (size <= 0) {
        return -EINVAL;
    }
    mytbf_st* me = static_cast<mytbf_st*>(ptr);
    me->token += size;
    if (me->token > me->burst) {
        me->token = me->burst;
    }
    return size;
}

int mytbf_destroy(mytbf_t* ptr) {
    mytbf_st* me = static_cast<mytbf_st*>(ptr);
    // TODO 注意: 设计上的重点！
    job[me->pos] = nullptr;
    free(ptr);

    return 0;
}
